package com.tian.netty.core;

import com.tian.netty.message.AbstractMsg;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.bytes.ByteArrayDecoder;

import java.util.List;

/**
 * 做TCP粘包和拆包的处理
 *  ByteArrayDecoder在Netty中的作用是将接收到的字节数据转换成字节数组。具体来说，它在数据处理流程中扮演着以下角色：
 * 1、数据传输的核心：在Netty框架中，数据传输的核心是ByteBuf，它提供了多种数据的读写方法。ByteArrayDecoder利用这些方法将ByteBuf中的数据转换为应用程序能够理解的字节数组格式。
 * 2、序列化与反序列化：在网络通信中，发送端通常将对象信息序列化成字节数据后发送，而接收端则需要将这些字节数据反序列化回对象信息。ByteArrayDecoder就是负责在接收端执行这一反序列化过程的关键组件。
 * 3、解码器的使用：在Netty的ChannelPipeline中，通常会先添加FrameDecoder来处理数据的分帧问题，然后再添加ByteArrayDecoder来进行实际的数据转换工作。这种组合方式确保了数据的正确解析和转换。
 * 4、转换格式：除了将ByteBuf转换为字节数组，Netty还提供了其他类型的解码器，如StringDecoder和Base64Decoder，用于将ByteBuf转换为字符串或其他格式。ByteArrayDecoder是这些解码器中的一种，专门用于字节数组的转换。
 * 总的来说，ByteArrayDecoder在Netty中的作用是将接收到的字节数据转换成字节数组，它是Netty框架中处理字节数据的关键组件之一，通过它将ByteBuf中的字节数据转换为应用程序可以处理的字节数组，从而实现了数据的传输和处理。
 */
public class MsgByteArrayDecoder extends ByteArrayDecoder {
    
    byte[] buffer;
    
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        //创建了一个字节数组temp，其大小与msg中当前可读字节的数量相同。
        byte[] temp = new byte[msg.readableBytes()];
        //从msg的起始位置读取数据到temp数组。
        msg.getBytes(0, temp);

        //判断buffer是否为空
        if(buffer != null) {
            //清空msg的内容
            msg.clear();
            //将buffer的内容写入msg
            msg.writeBytes(buffer);
            //将temp的内容也写入msg
            msg.writeBytes(temp);
            //将buffer设置为null
            buffer = null;
        }
        //获取msg中当前可读字节的长度
        int readalbleLength = msg.readableBytes();
        //检查msg的长度是否满足最小长度 14，并且检查其第一个字节是否为 0x68 的头部字节。
        //十六进制数 0x68 对应的十进制数是 104
        if(readalbleLength >= AbstractMsg.MIN_MSG_LEN && msg.getByte(0) ==  AbstractMsg.MSG_HEAD) {

            int frameLength = 0;
            
            int index =  msg.readerIndex();
            //循环读取
            while(index < readalbleLength) {
                //计算帧长度
                //十六进制数 0xFF 对应的十进制数是 255
                //十六进制数 0xFF00 对应的十进制数是 65280。
                frameLength = (msg.getByte(index + 1) & 0xFF) + (msg.getByte(index + 2) << 8 & 0xFF00);
                //如果有足够的字节，则提取该帧并添加到`out`集合中
                if(index + 3 + frameLength <= readalbleLength) {
                    //取出自己定义的packet包返回给ChannelRead
                    ByteBuf frame = msg.slice(index, 3 + frameLength).retain();
                    
                    byte[] array = new byte[frame.readableBytes()];
                    frame.getBytes(0, array);
                    out.add(array);  
                    //更新`index`以指向下一个帧的起始位置
                    index += frameLength + 3;

                    //这一步一定要有，不然其实bytebuf的readerIndex没有变，netty会一直从这里开始读取，将readerIndex移动就相当于把前面的数据处理过了废弃掉了。
                    //更新`msg`的`readerIndex`以标记已处理的数据
                    msg.readerIndex(index);
                    //释放`frame`对象的引用
                    frame.release();
                } else {
                    //如果当前读取的位置之后没有足够的字节来构成一个完整的帧，代码会创建一个新的buffer来保存剩余的字节，并跳出循环。
                    buffer = new byte[msg.readableBytes()];
                    msg.readBytes(buffer);
                    break;
                }
            }
        }
    }
}
